﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using ServoDrivesLib.Model;
using ServoDrivesLibAbstract;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace ServoDrivesLib.Controls
{
    public class ZMoitionControl : CardControl
    {
        private readonly StringBuilder builder = new StringBuilder(2048, 2048);
        private IntPtr _handle = IntPtr.Zero;
        private readonly string _ipaddr;
        private UdpClient _udpClient;
        private ILogger<ZMoitionControl>? _logger;
        private readonly byte[] EnableValue = { 03, 0x0b, 00, 00, 00, 12, 0xda, 0x7f, 0x03, 00, 00 };

        private readonly uint _slot = 0;
        public override IEtherCATMode EtherCATControl => this;

        public override IServoControl ServoControl => this;

        internal override ILogger? Logger => _logger;

        public ZMoitionControl(ILogger<ZMoitionControl>? logger, IConfiguration? configuration) : base()
        {
            base.LogMessage("");
            _logger = logger;
            _ipaddr = configuration?.GetRequiredSection("ZMoition:IP").Value;

            base.CopyDll("zauxdll.dll");
            base.CopyDll("zmotion.dll");
        }
        public override int ClearAxisError(int axis)
        {
            var ret = zmcaux.ZAux_DirectCommand(_handle, "DATUM(0)", builder, 2048);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int ClearErrorCode()
        {
            return this.Init();
        }

        public override int ClearPosition(int axis)
        {
            var ret = zmcaux.ZAux_Direct_SetMpos(_handle, axis, 0);
            base.LogMessage(ret.ToString());
            return ret;
        }
        public override int Close()
        {
            var ret = zmcaux.ZAux_Close(_handle);
            _udpClient?.Close();
            base.LogMessage(ret.ToString());
            return ret;
        }
        [Obsolete($"正运动控制没有{nameof(this.DownloadMemFile)}功能")]
        public override int DownloadMemFile()
        {
            throw new NotImplementedException();
        }
        [Obsolete($"正运动控制{nameof(this.EndScan)}功能与{nameof(this.StopEtherCAT)}功能重复")]
        public override int EndScan()
        {
            throw new NotImplementedException();
        }

        public override int GetAxisError(int axis, ref int error)
        {
            var ret = zmcaux.ZAux_Direct_GetAxisStatus(_handle, axis, ref error);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetAxisMoveStatus(int axis)
        {
            int status = 0;
            var ret = zmcaux.ZAux_Direct_GetIfIdle(_handle, axis, ref status);
            base.LogMessage(ret.ToString());
            return status;
        }

        public override int GetAxisNums(ref int axisNums)
        {
            axisNums = 0;
            int slaveNums = 0;
            var ret = this.GetSlaveValue(ref slaveNums);
            for (int i = 0; i < slaveNums; i++)
            {
                builder.Remove(0, builder.Length);
                zmcaux.ZAux_Execute(_handle, $"?NODE_AXIS_COUNT(0,{i})", builder, 2048);
                axisNums += int.Parse(builder.ToString());
            }
            return ret;
        }

        public override int GetAxisSpeed(int axis, ref double speed)
        {
            float d = 0f;
            var ret = zmcaux.ZAux_Direct_GetMspeed(_handle, axis, ref d);
            speed = d;
            base.LogMessage(ret.ToString());
            return ret;
        }
        public override int GetAxisStatus(int axis, ref int status)
        {
            var ret = zmcaux.ZAux_Direct_GetAxisEnable(_handle, axis, ref status);
            base.LogMessage(ret.ToString());
            if (status == 1)
            {
                status = 0x04;
            }
            return ret;
        }

        public override int GetAxisType(int axis, ref int type)
        {
            var ret = zmcaux.ZAux_Direct_GetAtype(_handle, axis, ref type);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetEncoderUnit(int axis, ref double encoder)
        {
            float d = 0f;
            var ret = zmcaux.ZAux_Direct_GetMpos(_handle, axis, ref d);
            encoder = d;
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetEquiv(int axis, ref double equip)
        {
            return this.GetEncoderUnit(axis, ref equip);
        }

        public override int GetErrorCode(ref int code)
        {
            uint status = 0;
            var ret = zmcaux.ZAux_BusCmd_GetNodeStatus(_handle, 0, 0, ref status);
            code = (int)status;
            base.LogMessage(ret.ToString());
            return ret;
        }
        [Obsolete($"正运动控制没有{nameof(this.GetEtherCATConsumeTime)}功能")]
        public override int GetEtherCATConsumeTime(ref ulong averageTime, ref ulong maxTime, ref ulong cycles)
        {
            throw new NotImplementedException();
        }

        public override int GetHomeMove(int axis, ref int status)
        {
            uint s = 0;
            var ret = zmcaux.ZAux_Direct_GetHomeStatus(_handle, axis, ref s);
            status = (int)s;
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetProFile(int axis, ref double minSpeed, ref double maxSpeed, ref double tAcc, ref double tDec, ref double stopSpeed, ref double sramp)
        {
            float a = 0;
            var ret = zmcaux.ZAux_Direct_GetAccel(_handle, axis, ref a);
            tAcc = a;
            base.LogMessage(ret.ToString());
            a = 0;
            ret = zmcaux.ZAux_Direct_GetDecel(_handle, axis, ref a);
            tDec = a;
            base.LogMessage(ret.ToString());
            a = 0;
            maxSpeed = a;
            ret = zmcaux.ZAux_Direct_GetSpeed(_handle, axis, ref a);
            base.LogMessage(ret.ToString());
            a = 0;
            minSpeed = a;
            ret = zmcaux.ZAux_Direct_GetLspeed(_handle, axis, ref a);
            base.LogMessage(ret.ToString());
            a = 0;
            sramp = a;
            ret = zmcaux.ZAux_Direct_GetSramp(_handle, axis, ref a);
            base.LogMessage(ret.ToString());

            return ret;
        }

        public override int GetSlaveValue(ref int slaveValue)
        {
            var ret = zmcaux.ZAux_BusCmd_GetNodeNum(_handle, 0, ref slaveValue);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int Init()
        {
            int ret = 0;
            var ip = new IPEndPoint(IPAddress.Parse(_ipaddr), 1010);
            _udpClient = new(_ipaddr, 1010);
            _udpClient.Send(this.EnableValue);

            var data = _udpClient.Receive(ref ip);
            zmcaux.ZAux_OpenEth(_ipaddr, out _handle);
            base.LogMessage("初始化");
            if (_handle == IntPtr.Zero)
            {
                ret = 0;
            }
            else
            {
                if (data != null)
                {
                    if (data[9] == 0)
                    {
                        ret = 1;
                        base.LogMessage($"总线初始化失败:{data[9]}");
                    }
                }
            }
            return ret;
        }

        public override int MoveAxis(int axis, double position)
        {
            if (position is > float.MaxValue or < float.MinValue)
            {
                throw new NotImplementedException();
            }
            var ret = zmcaux.ZAux_Direct_Single_Move(_handle, axis, (float)position);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int MoveAxisAbs(int axis, double position)
        {
            if (position is > float.MaxValue or < float.MinValue)
            {
                throw new NotImplementedException();
            }
            var ret = zmcaux.ZAux_Direct_Single_MoveAbs(_handle, axis, (float)position);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int MoveStop(int axis)
        {
            var ret = zmcaux.ZAux_Direct_Single_Cancel(_handle, axis, 0);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int PDORead(int slave, int index, int subIndex, int valueLength, ref int value)
        {
            int ret = 0;
            ret = zmcaux.ZAux_BusCmd_NodePdoRead(_handle, (uint)slave, (uint)index, (uint)subIndex, (uint)valueLength, ref value);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int PDOWrite(int slave, int index, int subIndex, int valueLength, int value)
        {
            int ret = 0;
            ret = zmcaux.ZAux_BusCmd_NodePdoWrite(_handle, (uint)slave, (uint)index, (uint)subIndex, (uint)valueLength, value);
            base.LogMessage(ret.ToString());
            return ret;
        }

        [Obsolete($"正运动控制没有{nameof(this.Rest)}功能")]
        public override int Rest(CardResetStatus status)
        {
            throw new NotImplementedException();
        }

        public override int SDORead(int slave, int index, int subIndex, int valueLength, ref int value)
        {
            int ret = 0;
            ret = zmcaux.ZAux_BusCmd_SDORead(_handle, _slot, (uint)slave, (uint)index, (uint)subIndex, (uint)valueLength, ref value);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int SDOWrite(int slave, int index, int subIndex, int valueLength, int value)
        {
            int ret = 0;
            ret = zmcaux.ZAux_BusCmd_SDOWrite(_handle, _slot, (uint)slave, (uint)index, (uint)subIndex, (uint)valueLength, value);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int SetAxisDisable(int axis)
        {
            var ret = zmcaux.ZAux_Direct_SetAxisEnable(_handle, axis, 0);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int SetAxisEnable(int axis)
        {
            var ret = zmcaux.ZAux_Direct_SetAxisEnable(_handle, axis, 1);
            base.LogMessage(ret.ToString());
            return ret;
        }
        [Obsolete($"正运动控制没有{nameof(this.SetCycleTime)}功能")]
        public override int SetCycleTime(int cycleTime)
        {
            throw new NotImplementedException();
        }

        public override int SetEquiv(int axis, double equip)
        {
            var ret = zmcaux.ZAux_Direct_SetUnits(_handle, axis, (float)equip);
            return ret;
        }

        public override int SetHomeMove(int axis)
        {
            var ret = zmcaux.ZAux_BusCmd_Datum(_handle, (uint)axis, 0);
            base.LogMessage(ret.ToString());
            return ret;
        }

        public override int SetProFile(int axis, double minSpeed, double maxSpeed, double tAcc, double tDec, double stopSpeed, double sramp)
        {
            var ret = zmcaux.ZAux_Direct_SetAccel(_handle, axis, (float)tAcc);
            base.LogMessage(ret.ToString());
            ret = zmcaux.ZAux_Direct_SetDecel(_handle, axis, (float)tDec);
            base.LogMessage(ret.ToString());
            ret = zmcaux.ZAux_Direct_SetSpeed(_handle, axis, (float)maxSpeed);
            base.LogMessage(ret.ToString());
            ret = zmcaux.ZAux_Direct_SetLspeed(_handle, axis, (float)minSpeed);
            base.LogMessage(ret.ToString());
            ret = zmcaux.ZAux_Direct_SetSramp(_handle, axis, (float)sramp);
            base.LogMessage(ret.ToString());

            return ret;
        }

        public override int StartScan()
        {
            return this.Init();
        }

        public override int StopEtherCAT()
        {
            var ret = zmcaux.ZAux_DirectCommand(_handle, "SLOT_STOP()", builder, 2048);
            base.LogMessage(ret.ToString());
            return ret;
        }
    }
}
